﻿
namespace Sai.Test.Rules
{
    using System.Linq;
    using Sai.Core;
    using Sai.Core.Rules;
    using Sai.Test.TestDataBuilders;
    using Xunit;
    using NSubstitute;
    using Sai.Test.XUnit;

	public class CheckClassPropertiesFacts
	{
		[Fact, TouchingFileSystemTest]
		public void Removing_Single_Property_Is_Flagged()
		{
			var oldClassBuilder = CompilableClassBuilder.PublicClass()
										.Named("Account")
										.WithProperty("public string Text { get; set; }");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

			var newClassBuilder = CompilableClassBuilder.PublicClass()
										.Named("Account");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

			TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeProperties())
                .Build();

			Assert.True(diff.HasChanged);

			Assert.Equal(1, diff.PropertiesRemoved.Count);
            Assert.Equal(TypeChangeReason.PropertyRemoved, diff.PropertiesRemoved.First().Reason);
		}

        [Fact, TouchingFileSystemTest]
        public void Adding_Single_Property_Is_Flagged()
        {
            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public string Text { get; set; }");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeProperties())
                .Build();

            Assert.True(diff.HasChanged);
            Assert.Equal(1, diff.PropertiesAdded.Count);
            Assert.Equal(TypeChangeReason.PropertyAdded, diff.PropertiesAdded.First().Reason);
        }

        [Fact, TouchingFileSystemTest]
        public void Removing_Property_Public_Set_Is_Flagged()
        {
            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public string Text { get; set; }");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public string Text { get; private set; }");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeProperties())
                .Build();

            Assert.True(diff.HasChanged);
            Assert.Null(diff.PropertiesAdded);
            Assert.Null(diff.PropertiesRemoved);
            Assert.Equal(1, diff.PropertiesChanged.Count);
            Assert.Equal(TypeChangeReason.PropertyChanged, diff.PropertiesChanged.First().Reason);
        }

        
        [Fact, TouchingFileSystemTest]
        public void Removing_Property_Public_Get_Is_Flagged()
        {
            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public string Text { get; set; }");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public string Text { private get; set; }");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeProperties())
                .Build();

            Assert.True(diff.HasChanged);
            Assert.Equal(1, diff.PropertiesChanged.Count);
            Assert.Equal(TypeChangeReason.PropertyChanged, diff.PropertiesChanged.First().Reason);
        }

        
        [Fact, TouchingFileSystemTest]
        public void Changing_Property_Type_Is_Flagged()
        {
            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public string Text { get; set; }");

            string oldCode = BoilerPlateCodeBuilder.BuildFor(oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                                        .Named("Account")
                                        .WithProperty("public int Text { get; set; }");

            string newCode = BoilerPlateCodeBuilder.BuildFor(newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeProperties())
                .Build();

            Assert.True(diff.HasChanged);
            Assert.Null(diff.PropertiesAdded);
            Assert.Null(diff.PropertiesRemoved);
            Assert.Equal(1, diff.PropertiesChanged.Count);
            Assert.Equal(TypeChangeReason.PropertyChanged, diff.PropertiesChanged.First().Reason);
        }

        [Fact, TouchingFileSystemTest]
        public void Adding_Indexer_Is_Flagged()
        {
            const string className = "Class1";
            const string namespaceName = "ClassLibrary1";
            const string publicIndexer = "public string this[int i] { get { return string.Empty; } }";

            var oldClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className);

            string oldCode = BoilerPlateCodeBuilder.BuildFor(namespaceName, oldClassBuilder);

            var newClassBuilder = CompilableClassBuilder.PublicClass()
                .Named(className)
                .WithMethod(publicIndexer);

            string newCode = BoilerPlateCodeBuilder.BuildFor(namespaceName, newClassBuilder);

            IProductReleaseProject project = Mock.MinimalProject();

            TypeDifference diff = TypeDifferenceBuilder.Differences()
                .CodeWas(oldCode)
                .CodeIsNow(newCode)
                .CheckedUsing(new CheckTypeProperties())
                .Build();

            Assert.True(diff.HasChanged);

            Assert.Equal(1, diff.PropertiesAdded.Count);
            Assert.Null(diff.PropertiesRemoved);
            Assert.Null(diff.PropertiesObsolete);
            Assert.Null(diff.PropertiesChanged);

            UnaryTypeChange change = diff.PropertiesAdded.First();

            Assert.Equal(TypeChangeReason.PropertyAdded, change.Reason);
        }

	}
}
